﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.ComponentModel;
using System.Activities.Presentation.PropertyEditing;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace FileTranslator
{
    // 此类是作为ProperGrid当做数据源。具体的参看http://www.codeproject.com/Articles/87715/Native-WPF-4-PropertyGrid
    public class DataRowForPropGrid : ICustomTypeDescriptor, INotifyPropertyChanged
    {
        private DataRow dataRow;
        private ConstraintHelper constraintHelper;

        public DataRowForPropGrid(DataRow rd, ConstraintHelper constraintHelper)
        {
            this.dataRow = rd;
            this.constraintHelper = constraintHelper;
        }

        #region ICustomTypeDescriptor Members
        public AttributeCollection GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, true);
        }
        public string GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }
        public string GetComponentName()
        {
            return null;
        }
        public TypeConverter GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }
        public EventDescriptor GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }
        public PropertyDescriptor GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, true);
        }
        public object GetEditor(Type editorBaseType)
        {
            return null;
        }
        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }
        public EventDescriptorCollection GetEvents()
        {
            return TypeDescriptor.GetEvents(this, true);
        }
        public object GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return this.GetProperties();
        }

        // 抽取感兴趣的Cell
        // 默认的行为是，除了与外表的主键对应的键是可以编
        // 辑之外，其他的与外表进行对应的键都是readonly的。
        public PropertyDescriptorCollection GetProperties()
        {
            // 由于在处理所有的PropertyGrid项之前，本函数都需要被调用，所以先将表示“已经处理到
            // 第几个特殊的列”的参数置为0
            GlobalPropertyGridCustomEditorParameter.Count = 0;
            GlobalPropertyGridCustomEditorParameter.Parameters.Clear();

            var props = new PropertyDescriptorCollection(null);

            // 针对每个Cell都加入到一个属性项
            for(int i = 0; i < dataRow.Table.Columns.Count; i++)
            {
                string columnName = dataRow.Table.Columns[i].ColumnName;
                bool isReadOnly = false;
                bool useComboBox = false;
                string foreignColumnName = "";
                DataTable referenceTable = null;
                foreach (ConstraintHint ch in constraintHelper.OtherConstraints)
                {
                    // 先检查是否直接就有ReadOnly约束(常量约束本身就代表了Readonly)
                    AllCellSameValueConstraint roc = ch as AllCellSameValueConstraint;
                    if (roc != null)
                    {
                        // 作为一个readonly的列，它只有显示的功能，所以没必要有做外键ComboBox给予选择的功能了
                        isReadOnly = true;
                        break;
                    }

                    ForeignKeyConstraint fkc = ch as ForeignKeyConstraint;
                    if (fkc != null)
                    {
                        if (fkc.AssociatedColumns.ContainsKey(columnName) == true)
                        {
                            if (fkc.AssociatedColumns[columnName] == fkc.OtherTablePrimaryKeyColumnName)
                            {
                                // 外键将会使用ComboBox来展现
                                useComboBox = true;
                                foreignColumnName = fkc.OtherTablePrimaryKeyColumnName;
                                referenceTable = fkc.ReferenceTable;
                            }
                            else
                            {
                                // 将本表中对应的，外键所对应的表中列设置为readonly，只有外键不设置为readonly
                                isReadOnly = true;
                                // 从外键所在表看外键是一对一的，所以一个列最多只有一个外键约束，可以使用break
                                break;
                            }
                        }
                    }
                }

                List<Attribute> attributes = new List<Attribute>();

                if (useComboBox == true)
                {
                    // 自定义供ComboBox所使用的全局参数
                    GlobalPropertyGridCustomEditorParameter.Parameter parm
                        = new GlobalPropertyGridCustomEditorParameter.Parameter();
                    parm.referenceSourceTable = referenceTable;
                    parm.thisSourceTable = dataRow.Table;
                    parm.currentRow = dataRow;
                    parm.selectedValueName = columnName;
                    parm.displayMemberPath = foreignColumnName;
                    parm.selectedValuePath = foreignColumnName;
                    GlobalPropertyGridCustomEditorParameter.Parameters.Add(parm);

                    // 为此列专门做Editor的重载，以自定义Editor
                    attributes.Add(
                        new EditorAttribute(
                            typeof(ComboBoxSupportEditor), 
                            typeof(PropertyValueEditor)) );
                }

                attributes.Add(new DisplayNameAttribute(columnName));
                attributes.Add(new DefaultValueAttribute(dataRow[columnName].ToString()));

                PropertyDescriptor prop =
                    new DataCellPropertyDescriptor(
                        columnName, 
                        dataRow, 
                        isReadOnly,
                        attributes.ToArray<Attribute>());
                props.Add(prop);
            }
            
            return props;
        }
        #endregion

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
        #endregion
    }

    internal class DataCellPropertyDescriptor : PropertyDescriptor
    {
        private DataRow dataRow;
        private bool isReadOnly;
        private string column;

        public DataCellPropertyDescriptor(
            string columnName, 
            DataRow dataRow,
            bool isReadOnly,
            Attribute[] attributes)
            : base(columnName, attributes)
        {
            this.column = columnName;
            this.dataRow = dataRow;
            this.isReadOnly = isReadOnly;
        }

        public override bool CanResetValue(object component)
        {
            return false;
        }

        public override Type ComponentType
        {
            get { return typeof(string); }
        }

        public override object GetValue(object component)
        {
            return dataRow[column];
        }

        public override bool IsReadOnly
        {
            get { return isReadOnly; }
        }

        public override Type PropertyType
        {
            get { return typeof(string); }
        }

        public override void ResetValue(object component)
        {
        }

        public override void SetValue(object component, object value)
        {
            this.dataRow[column] = (string)value;
        }

        public override bool ShouldSerializeValue(object component)
        {
            // 所有的保存工作都已经在SetValue中进行了（因为传递了引用的关系）
            return false;
        }
    }

    // 该类用作全局变量，用来区别是哪个column需要特殊处理
    // 此类的使用具有一定的局限性
    internal static class GlobalPropertyGridCustomEditorParameter
    {
        // 在自定义Editor中用于计数
        public static int count = 0;
        public static int Count 
        {
            get
            {
                return count;
            }
            set { count = value; }
        }

        public struct Parameter
        {
            public DataTable thisSourceTable;     // 默认值所在的数据源表
            public DataTable referenceSourceTable;// 本ComboBox所对应的数据源表

            public string displayMemberPath; // 本ComboBox下拉表中所需要显示地内容
            public string selectedValuePath; // 本ComboBox下来表中各项所对应的值
            public string selectedValueName; // 本ComboBox所显示地是dataTable中的哪一列
            public DataRow currentRow;       // 目前的cell所需要处理的数据行
        }

        static private List<Parameter> parameters = new List<Parameter>();
        static public List<Parameter> Parameters 
        {
            get
            {
                return parameters;
            }
        }
    }

    // 自定义PropertyGrid中的一行的格式，该类型将会在PropertyGrid显示前被调用
    internal class ComboBoxSupportEditor : ExtendedPropertyValueEditor
    {
        public ComboBoxSupportEditor()
        {
            GlobalPropertyGridCustomEditorParameter.Parameter parm = 
                GlobalPropertyGridCustomEditorParameter.Parameters[GlobalPropertyGridCustomEditorParameter.Count];

            DataTemplate datatemplate = new DataTemplate();
            FrameworkElementFactory fef = new FrameworkElementFactory(typeof(ComboBox));
            Binding binding = new Binding();
            binding.Source = parm.referenceSourceTable.DefaultView;
            fef.SetBinding(ComboBox.ItemsSourceProperty, binding);
            Binding binding2 = new Binding();
            binding2.Source = parm.thisSourceTable.DefaultView[parm.currentRow.Table.Rows.IndexOf(parm.currentRow)];
            fef.SetBinding(ComboBox.DataContextProperty, binding2);
            

            Binding selectVal = new Binding(parm.selectedValueName);
            selectVal.Mode = BindingMode.TwoWay;
            fef.SetBinding(ComboBox.SelectedValueProperty, selectVal);

            fef.AddHandler(ComboBox.SelectionChangedEvent,
                (SelectionChangedEventHandler)delegate(object sender2, SelectionChangedEventArgs e2)
                {
                    ComboBox cb = sender2 as ComboBox;

                    if (cb != null && cb.SelectedValue != null)
                    {
                        parm.currentRow[parm.selectedValueName] = cb.SelectedValue.ToString();
                    }
                });

            fef.SetValue(ComboBox.DisplayMemberPathProperty, parm.displayMemberPath);
            fef.SetValue(ComboBox.SelectedValuePathProperty, parm.selectedValuePath);

            datatemplate.VisualTree = fef;
            this.InlineEditorTemplate = datatemplate;


            this.ExtendedEditorTemplate = datatemplate;

            // 递增已经处理过的数目
            GlobalPropertyGridCustomEditorParameter.Count++;
        }
    }
    
}
